using OfficeOpenXml;
using System.IO;
using UnityEditor;
using System.Text;
using System;
using LitJson;
using System.Text.RegularExpressions;
using Fusion.Utilities.IO;
using System.Collections.Generic;
using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup;

namespace Fusion.Frameworks.Excel.Editor
{
    public class ExcelConverter
    {
        [MenuItem("Excels/Build", priority = 4000)]
        public static void Build()
        {
            BuildClass();
            BuildJson();
        }


        [MenuItem("Excels/BuildClass")]
        public static void BuildClass()
        {
            if (!Directory.Exists("Excels"))
            {
                return;
            }

            if (Directory.Exists("Assets/Scripts/DataStorages"))
            {
                string[] files = Directory.GetFiles("Assets/Scripts/DataStorages");
                foreach (string file in files)
                {
                    File.Delete(file);
                }
            } else
            {
                Directory.CreateDirectory("Assets/Scripts/DataStorages");
            }

            string[] excels = Directory.GetFiles("Excels");
            foreach (string file in excels)
            {
                FileInfo fileInfo= new FileInfo(file);
                if (fileInfo.Name.EndsWith(".xlsx") && !fileInfo.Name.StartsWith("~$"))
                {
                    ConvertExcel(file);
                }
            }

            AssetDatabase.Refresh();
            UnityEngine.Debug.Log("Class文件生成结束");
        }

        [MenuItem("Excels/BuildJson")]
        public static void BuildJson()
        {
            if (!Directory.Exists("Excels"))
            {
                return;
            }

            if (Directory.Exists("Assets/GameAssets/DataStorages"))
            {
                string[] files = Directory.GetFiles("Assets/GameAssets/DataStorages");
                foreach (string file in files)
                {
                    if (file.EndsWith(".json"))
                        File.Delete(file);
                }
            }
            else
            {
                Directory.CreateDirectory("Assets/GameAssets/DataStorages");
            }

            string[] excels = Directory.GetFiles("Excels");
           
            foreach (string file in excels)
            {
                FileInfo fileInfo = new FileInfo(file);
                if (fileInfo.Name.EndsWith(".xlsx") && !fileInfo.Name.StartsWith("~$"))
                {
                    GenerateJsonData(file);
                }
            }
           
            AssetDatabase.Refresh();
            UnityEngine.Debug.Log("Json文件生成结束");
        }

        private static void ConvertExcel(string path)
        {
            FileInfo fileInfo = new FileInfo(path);

            ExcelPackage excelPackage = new ExcelPackage(fileInfo);

            ExcelWorksheet excelWorkbook = excelPackage.Workbook.Worksheets[1];
            
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(@"using System.Collections.Generic;
using Fusion.Frameworks.Assets;
using LitJson;
#if FUSION_DYNAMIC_DLL
using JsonMapper = LitJson.DynamicDLL.JsonMapper;
#endif
using UnityEngine;
using System;
using Fusion.Utilities.Generics;

namespace Fusion.Frameworks.DataStorages
{");
            string className = fileInfo.Name.Substring(0, fileInfo.Name.LastIndexOf("."));
            GenerateClass(ref stringBuilder, excelWorkbook, className);

            stringBuilder.Append(@$"
    public class {className}Storage {{
        private static OrderDictionary<int, {className}> storage = null;
        private static string dataPath = ""DataStorages/{className}"";

        public static OrderDictionary<int, {className}> Storage
        {{
            get
            {{
                if (storage == null)
                {{
                    Init();
                }}

                return storage;
            }}
        }}

        public static {className} Get(int id)
        {{
            {className} result;
            Storage.TryGetValue(id, out result);
            return result;
        }}

        private static void Init() 
        {{
            TextAsset textAsset = AssetsManager.Instance.Load<TextAsset>(dataPath);
            List<{className}> storageTemp = JsonMapper.ToObject<List<{className}>>(textAsset.text);
            storage = new OrderDictionary<int, {className}>();
            foreach (var example in storageTemp)
            {{
                storage.Add(example.id, example);
            }}
            AssetsUtility.ReleaseImmediate(dataPath);
        }}

        public static int Count()
        {{
            return Storage.Count;
        }}

        public static List<{className}> GetList(Func<{className}, bool> condition = null, Comparison<{className}> comparison = null)
        {{
            List<{className}> list = new List<{className}>();
            for (int index=0; index < Storage.Count; index++)
            {{
                {className} data = Storage.Get(index);
                if (condition == null || condition(data)) 
                {{
                    list.Add(data);
                }}
            }}
            if (comparison != null)
            {{
                list.Sort(comparison);
            }}
            return list;
        }}

        public static void Clear()
        {{
            storage?.Clear();
            storage = null;
        }}
    }}
");
            stringBuilder.Append(@"
}");
            IOUtility.Write($"Assets/Scripts/DataStorages/{className}Storage.cs", stringBuilder.ToString());
            excelPackage.Dispose();
        }

        
        public struct NameData
        {
            // 基本类型  Class  List  Symbol
            public string type;
            public string name;

            public NameData(string type, string name)
            {
                this.type = type;
                this.name = name;
            }
        }
        static Stack<NameData> nameStack = new Stack<NameData> ();
        private static void GenerateClass(ref StringBuilder stringBuilder, ExcelWorksheet sheet, string className)
        {
            stringBuilder.Append($@"
    public struct {className}
    {{
");
            int index = 1;
            string type = sheet.Cells[1, index].Value?.ToString();
            StringBuilder sb = new StringBuilder();
            nameStack.Clear();
            while (type != null)
            {
                string name = sheet.Cells[2, index].Value?.ToString();
                bool typeHandled = false;
                if (name == null)
                {
                    index++;
                    type = sheet.Cells[1, index].Value?.ToString();
                    continue;
                }

                int nameType = 0; // 0 基本 1 类 2 数组

                for (int i = 0; i < name.Length; i++)
                {
                    if (name[i] == '{' || name[i] == '[')
                    {
                        // 处理类或数组
                        nameStack.Push(new NameData(name[i] == '{' ? "Class" : "List", sb.ToString())) ;
                        nameStack.Push(new NameData("Symbol", name[i].ToString()));
                        sb.Clear();
                    }
                    else if (name[i] == '}' || name[i] == ']')
                    {
                        //遇到类或数组的结束符号
                        if (typeHandled == false)
                        {
                            nameStack.Push(new NameData(sheet.Cells[1, index].Value.ToString(), sb.ToString()));
                            typeHandled = true;
                        }

                        sb.Clear();
                        //类的结束符
                        if (name[i] == '}')
                        {
                            nameType = 1;
                            sb.Append(")");
                            NameData nameData = nameStack.Pop();
                            while (nameData.name != "{")
                            {
                                string typeString = (nameData.type != "Class" && nameData.type != "List") ? nameData.type : "";
                                sb.Insert(0, $", {typeString} {nameData.name}");
                                nameData = nameStack.Pop();
                            }
                            sb.Remove(0, 2);
                            sb.Insert(0, "(");

                            nameData = nameStack.Pop();
                            if (nameData.type == "Class")
                            {
                                sb.Append($" {nameData.name}");
                            }
                        }
                        else if (name[i] == ']')
                        {
                            nameType = 2;
                            NameData nameData = nameStack.Pop();
                            string typeString = (nameData.type == "Class" || nameData.type == "List") ? nameData.name : nameData.type;
                            while (nameData.name != "[")
                            {
                                nameData = nameStack.Pop();
                            }

                            sb.Append($"List<{typeString}>");
                            nameData = nameStack.Pop();
                            if (nameData.type == "List")
                            {
                                sb.Append($" {nameData.name}");
                            }
                        }

                        if (nameStack.Count == 0)
                        {
                            stringBuilder.Append($"\tpublic {sb};\n");
                        }
                        else
                        {
                            nameStack.Push(new NameData("List", sb.ToString()));
                        }
                        sb.Clear();
                    }
                    else
                    {
                        sb.Append(name[i]);
                    }
                }

                if (nameType == 0)
                {
                    if (nameStack.Count == 0)
                    {
                        stringBuilder.Append($"\tpublic {sheet.Cells[1, index].Value} {sb};\n");
                    }
                    else
                    {
                        nameStack.Push(new NameData(sheet.Cells[1, index].Value.ToString(), sb.ToString()));
                    }
                    sb.Clear();
                }
                index++;
                type = sheet.Cells[1, index].Value?.ToString();
            }
            stringBuilder.Append(@"
    }");
        }

        private static void GenerateJsonData(string path)
        {
            FileInfo fileInfo = new FileInfo(path);

            ExcelPackage excelPackage = new ExcelPackage(fileInfo);

            ExcelWorksheet sheet = excelPackage.Workbook.Worksheets[1];
            string className = fileInfo.Name.Substring(0, fileInfo.Name.LastIndexOf("."));

            JsonData jsonData = new JsonData();
            jsonData.SetJsonType(JsonType.Array);
            int rowIndex = 3;
            while (true)
            {
                rowIndex++;
                if (sheet.Cells[rowIndex, 1].Value == null)
                {
                    break;
                }

                JsonData data;
                CreateDataObject(sheet, rowIndex, out data);
                jsonData.Add(data);
            }

            string result = Regex.Unescape(JsonMapper.ToJson(jsonData));
            IOUtility.Write($"Assets/GameAssets/DataStorages/{className}.json", result);
            excelPackage.Dispose();
        }


        static Stack<JsonData> jsonDataStack = new Stack<JsonData>();
        static Stack<int> itemIndexStack = new Stack<int>();
        private static void CreateDataObject(ExcelWorksheet sheet, int rowIndex, out JsonData jsonData)
        {
            jsonData = new JsonData();
            int index = 1;
            string type = sheet.Cells[1, index].Value?.ToString();
            jsonDataStack.Clear();
            itemIndexStack.Clear();
            StringBuilder sb = new StringBuilder();
            int itemIndex = -1;
            while (type != null)
            {
                sb.Clear();
                string name = sheet.Cells[2, index].Value?.ToString();
                int endCount = 0;
                int length = name == null ? 0 : name.Length;
                for (int i = 0; i < length; i++)
                {
                    if (name[i] == '{' || name[i] == '[')
                    {
                        // 处理类或数组
                        JsonData newJsonData = new JsonData();
                        if (jsonData.GetJsonType() == JsonType.Array)
                        {
                            jsonData.Add(newJsonData);
                        }
                        else
                        {
                            if (jsonDataStack.Count > 0)
                            {
                                jsonData[$"Item{itemIndex}"] = newJsonData;
                                itemIndex++;
                            }
                            else
                            {
                                jsonData[sb.ToString()] = newJsonData;
                            }
                        }
                        itemIndexStack.Push(itemIndex);
                        jsonDataStack.Push(jsonData);
                        itemIndex = 1;
                        jsonData = newJsonData;
                        sb.Clear();

                        if (name[i] == '[')
                        {
                            jsonData.SetJsonType(JsonType.Array);
                        }
                    }
                    else if (name[i] == '}' || name[i] == ']')
                    {
                        //遇到类或数组的结束符号
                        endCount++;
                    }
                    else
                    {
                        sb.Append(name[i]);
                    }
                }

                if (jsonData.GetJsonType() == JsonType.Array)
                {
                    jsonData.Add(GetJsonDataValue(sheet.Cells, rowIndex, index, type));
                }
                else
                {
                    if (jsonDataStack.Count > 0)
                    {
                        jsonData[$"Item{itemIndex}"] = GetJsonDataValue(sheet.Cells, rowIndex, index, type);
                        itemIndex++;
                    }
                    else
                    {
                        jsonData[sb.ToString()] = GetJsonDataValue(sheet.Cells, rowIndex, index, type);
                    }
                }

                for (int i = 0; i < endCount; i++)
                {
                    itemIndex = itemIndexStack.Pop();
                    jsonData = jsonDataStack.Pop();
                }

                index++;
                type = sheet.Cells[1, index].Value?.ToString();
            }
        }

        public static JsonData GetJsonDataValue(ExcelRange cells, int rowIndex, int colIndex, string type)
        {
            return new JsonData(Convert.ChangeType(GetValue(cells, rowIndex, colIndex), GetTypeByString(type)));
        }

        public static string GetValue(ExcelRange cells, int rowIndex, int colIndex)
        {
            object data = cells[rowIndex, colIndex].Value;
            if (data == null)
            {
                int iterIndex = colIndex;
                object type = cells[1, colIndex].Value;
                while (type == null)
                {
                    iterIndex--;
                    type = cells[1, iterIndex].Value;
                }

                return GetTypeDefaultValue(type.ToString());
            }
            else
            {
                return data.ToString();
            }
        }

        public static string GetTypeDefaultValue(string type)
        {
            switch (type.ToLower())
            {
                case "bool":
                    return "true";
                case "double":
                    return "0";
                case "int":
                    return "0";
                case "long":
                    return "0";
                case "string":
                    return "";
                default:
                    return "";
            }
        }

        public static Type GetTypeByString(string type)
        {
            switch (type.ToLower())
            {
                case "bool":
                    return Type.GetType("System.Boolean", true, true);
                //case "byte":
                //    return Type.GetType("System.Byte", true, true);
                //case "sbyte":
                //    return Type.GetType("System.SByte", true, true);
                //case "decimal":
                //    return Type.GetType("System.Decimal", true, true);
                case "double":
                    return Type.GetType("System.Double", true, true);
                case "int":
                    return Type.GetType("System.Int32", true, true);
                //case "uint":
                //    return Type.GetType("System.UInt32", true, true);
                case "long":
                    return Type.GetType("System.Int64", true, true);
                //case "ulong":
                //    return Type.GetType("System.UInt64", true, true);
                //case "short":
                //    return Type.GetType("System.Int16", true, true);
                //case "ushort":
                //    return Type.GetType("System.UInt16", true, true);
                case "string":
                    return Type.GetType("System.String", true, true);
                default:
                    return Type.GetType(type, true, true);
            }
        }
    }
}
